# -*- coding:utf-8 -*-

import os
import sys
import time
import getpass
from PySide2.QtGui import *
from PySide2.QtCore import *
from PySide2.QtWidgets import *
try:
    import hou
except Exception as hou_ERR:
    print('testing')

import Deadline.DeadlineConnect as Connect
# try:
#     import DeadlineConnect as Connect
#     print('连接Deadline...')
# except Exception as Connect_ERR:
#     print('Connect_ERR:',Connect_ERR)
#     dir_path = os.path.dirname(__file__)
#     deadline_path = os.path.join(dir_path, 'Deadline_py3')
#     print(deadline_path)
#     sys.path.append(deadline_path)
#     import DeadlineConnect as Connect



class MultColumnWidget(QWidget):
    new_clients_Signal = Signal(dict)

    def __init__(self, clients_data, rowCount):
        """

        :param clients_data: {"name":status,"name":status,...}
        :param rowCount:  int , 每行个数
        """
        super(MultColumnWidget, self).__init__()

        self.btn_on_sty = """QPushButton{color:#e6e6e6}
                                  QPushButton:hover{color:red}
                                  QPushButton{border:2px}
                                  QPushButton{background-color:#3f9400}
                                  QPushButton{border-radius:0px}
                                  QPushButton{padding:0px 0px}"""
        self.btn_off_sty = """QPushButton{color:#e6e6e6}
                                  QPushButton:hover{color:red}
                                  QPushButton{border:2px}
                                  QPushButton{background-color:#666666}
                                  QPushButton{border-radius:0px}
                                  QPushButton{padding:0px 0px}"""
        self.setStyleSheet("background-color:#121212;color:rgb(200,200,200)")
        self.clients_data = clients_data

        self.main_layout = QHBoxLayout()

        self.init_elements(clients_data, rowCount)

        self.setLayout(self.main_layout)

    def clip_list(self, src_list, count):  # src_list为原列表，count为等分长度
        clip_back = []
        if len(src_list) > count:
            for i in range(int(len(src_list) / count)):
                clip_a = src_list[count * i:count * (i + 1)]
                clip_back.append(clip_a)
            # last 剩下的单独为一组
            last = src_list[int(len(src_list) / count) * count:]
            if last:
                clip_back.append(last)
        else:  # 如果切分长度不小于原列表长度，那么直接返回原列表
            clip_back = [src_list]  # 如果返回结构保持一致可以返回clip_back = [src_list]

        return clip_back

    def init_elements(self, clients_data, rowCount):
        if clients_data:
            name_list = list(clients_data.keys())
            clips = self.clip_list(name_list, rowCount)

            for clip in clips:
                vlayout = QVBoxLayout()
                vlayout.setAlignment(Qt.AlignTop)
                for name in clip:
                    btn = QPushButton(name)
                    btn.setStyleSheet(self.btn_on_sty if clients_data[name] else self.btn_off_sty)
                    btn.setProperty('status', clients_data[name])
                    # btn.clicked.connect(lambda x: self.name_Signal.emit(self.sender().text()))
                    btn.clicked.connect(self._choose)
                    vlayout.addWidget(btn)
                self.main_layout.addLayout(vlayout)

    def _choose(self):
        btn = self.sender()
        status = btn.property('status')
        status = False if status else True
        btn.setProperty('status', status)
        btn.setStyleSheet(self.btn_on_sty if status else self.btn_off_sty)
        self.clients_data[btn.text()] = status
        self.new_clients_Signal.emit(self.clients_data)
        # self.sender()
class JobList(QListWidget):
    job_id_Signal = Signal(str)

    def __init__(self,parent):
        super(JobList, self).__init__()
        palette = self.palette()
        palette.setColor(palette.Window, QColor('#393939'))
        self.setPalette(palette)
        self.parent = parent
        self.connectionObject = parent.connectionObject  # 内部操作吧
        self.job_status_color_map = parent.job_status_color_map
        self.jobs = []
        self.current_limits = {}  # 当前选择的job的机器名单 {'name':status,'name2':status,..}
                                    # status 为True或者False，
        self.current_job_id = ''  # 右击菜单栏时，当前选择的任务 job id
        self.setMinimumWidth(800)
        # self.itemClicked.connect(self._update_current_job_info)
        self.itemClicked.connect(self._select_current_index)
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.setContextMenuPolicy(Qt.CustomContextMenu)  # 开启自定义右击菜单
        self.customContextMenuRequested.connect(self.right_menu)


    def init_items(self,jobs):
        """
        传入即 简单的数据项目名称与颜色，生成item
        status_map = {0:'Unknown',1:'Active',2:'Suspended',3:'Completed',4:'Failed',6:'Pending'}
        jobs = {_id,data,...}
        """
        self.clear()
        if jobs:
            for _id,i in jobs.items():
                item = QListWidgetItem()
                item.setText(i['Name'])
                color = self.job_status_color_map[i['Stat']]
                # item.setBackground(QBrush(QColor(color)))
                item.setTextColor(QColor(color))
                item.setData(5, i)
                self.addItem(item)

    def init_machine_limit(self,machines):
        machine_info = {}
        if machines:
            for m in machines:
                machine_info[m] = True

        self.current_limits = machine_info


    def init_current_project_job(self,all_jobs):
        """
        all_jobs: {_id:data,_id:data,...}
        """
        self.jobs = all_jobs
        try:
            self._get_current_jobs()
        except Exception as init_current_project_job_ERR:
            print(init_current_project_job_ERR,'testing')

    def right_menu(self):
        self._update_current_job_info()
        rMenu = QMenu()
        rMenu.setStyleSheet("background-color:#121212;color:rgb(200,200,200)")
        currentProjectJobAction=QAction(u'查询当前Houdini工程Job',self)
        currentProjectJobAction.triggered.connect(self._get_current_jobs)
        rMenu.addAction(currentProjectJobAction)

        updateJobsAction = QAction('重新获取Deadline任务',self)
        updateJobsAction.triggered.connect(self._update_jobs)
        rMenu.addAction(updateJobsAction)

        rMenu.addSeparator()

        actvieJobsAction = QAction('查询我当前运行的Job',self)
        actvieJobsAction.triggered.connect(self._get_active_jobs)
        rMenu.addAction(actvieJobsAction)
        rMenu.addSeparator()

        allJobsAction = QAction('查询我的所有Job',self)
        allJobsAction.triggered.connect(self._get_all_jobs)
        rMenu.addAction(allJobsAction)

        failedJobsAction = QAction('查询我所有失败的Job',self)
        failedJobsAction.triggered.connect(self._get_failed_jobs)
        rMenu.addAction(failedJobsAction)

        completedJobsAction = QAction('查询我所有完成的Job',self)
        completedJobsAction.triggered.connect(self._get_completed_jobs)
        rMenu.addAction(completedJobsAction)

        suspendedJobsAction = QAction('查询我所有暂停的Job',self)
        suspendedJobsAction.triggered.connect(self._get_suspended_jobs)
        rMenu.addAction(suspendedJobsAction)

        pendingJobsAction = QAction('查询我所有关联依赖的Job',self)
        pendingJobsAction.triggered.connect(self._get_pending_jobs)
        rMenu.addAction(pendingJobsAction)

        rMenu.addSeparator()

        limitMenu = QMenu('给该任务添加黑名单机器',None)
        multActions = QWidgetAction(limitMenu)
        mw = MultColumnWidget(self.current_limits,10)
        mw.new_clients_Signal.connect(self._set_backlist)
        multActions.setDefaultWidget(mw)
        limitMenu.addAction(multActions)
        rMenu.addMenu(limitMenu)

        rMenu.addSeparator()

        resubAction = QAction('重提该任务',self)
        resubAction.triggered.connect(self._resub_job)
        rMenu.addAction(resubAction)

        rMenu.addSeparator()

        pasueAction = QAction('暂停该任务',self)
        pasueAction.triggered.connect(self._pause_job)
        rMenu.addAction(pasueAction)

        rMenu.addSeparator()

        deleteAction = QAction('删除该任务',self)
        deleteAction.triggered.connect(self._delete_job)
        rMenu.addAction(deleteAction)
        deleteAllFailedAction = QAction('删除我所有失败的任务',self)
        deleteAllFailedAction.triggered.connect(self._delete_all_failed_job)
        rMenu.addAction(deleteAllFailedAction)

        rMenu.exec_(QCursor.pos())

    def get_ListedSlaves(self,job_id):
        """
        根据job id即时查询当前选择任务的ListedSlaves信息
        """
        ListedSlaves = []
        job_info = self.connectionObject.Jobs.GetJob(job_id)
        if job_info:
            Props = job_info['Props']
            ListedSlaves = Props.get('ListedSlaves',[])

        return ListedSlaves

    def _get_current_jobs(self):
        """
        获取当前houdini 工程关联的job
        """
        hip_basename = hou.hipFile.basename()
        hip_name = os.path.splitext(hip_basename)[0]
        current_jobs = {}
        for _id,i in self.jobs.items():
            if hip_name in i['Name']:
                current_jobs[_id] = i

        self.init_items(current_jobs)



    def _update_jobs(self):
        """
        刷新获取最新的Deadline任务
        """
        self.jobs = self.parent.get_jobs_by_username()
        self._get_all_jobs()


    def _get_active_jobs(self):
        """
        获取当前正在运行的任务
        """
        active_jobs = {}
        for _id,i in self.jobs.items():
            if i['Stat'] == 1:
                active_jobs[_id] = i
        self.init_items(active_jobs)


    def _get_all_jobs(self):
        """
        获取该用户所有的job
        """
        self.init_items(self.jobs)



    def _get_failed_jobs(self):
        """
        获取所有失败的job
        """
        failed_jobs = {}
        for _id,i in self.jobs.items():
            if i['Stat'] == 4:
                failed_jobs[_id] = i 
        self.init_items(failed_jobs)
        return failed_jobs


    def _get_completed_jobs(self):
        """
        获取所有完成的job
        """
        completed_jobs = {}
        for _id,i in self.jobs.items():
            if i['Stat'] == 3:
                completed_jobs[_id] = i
        self.init_items(completed_jobs)
        return completed_jobs

    def _get_suspended_jobs(self):
        """
        获取所有暂停的job
        """
        suspended_jobs = {}
        for _id,i in self.jobs.items():
            if i['Stat'] == 2:
                suspended_jobs[_id] = i 
        self.init_items(suspended_jobs)
        return suspended_jobs


    def _get_pending_jobs(self):
        """
        获取所有关联依赖的job
        """
        pending_jobs = {}
        for _id,i in self.jobs.items():
            if i['Stat'] == 6:
                pending_jobs[_id] = i
        self.init_items(pending_jobs)
        return pending_jobs
        

    def _resub_job(self):
        """
        重新提交该任务
        """
        if not self.current_job_id:
            return
        result = self.connectionObject.Jobs.ResumeJob(self.current_job_id)
        if result == 'Success':
            color = self.job_status_color_map[1]
            current_item = self.currentItem()
            if current_item:
                # current_item.setBackground(QBrush(QColor(color)))
                current_item.setTextColor(QColor(color))


    def _pause_job(self):
        """
        暂停任务
        """
        if not self.current_job_id:
            return
        result = self.connectionObject.Jobs.SuspendJob(self.current_job_id)
        print('暂停任务结果:',result)
        if result == 'Success':
            color = self.job_status_color_map[2]
            current_item = self.currentItem()
            if current_item:
                # current_item.setBackground(QBrush(QColor(color)))
                current_item.setTextColor(QColor(color))
                print(f'设置当前行的字体颜色为{color}')

    def _delete_job(self):
        """
        删除该任务
        """
        if not self.current_job_id:
            return
        result = self.connectionObject.Jobs.DeleteJob(self.current_job_id)
        if result == 'Success':
            self.jobs.pop(self.current_job_id)
            self.takeItem(self.currentRow())
            

    def _delete_all_failed_job(self):
        """
        删除我所有失败的任务
        """
        failed_jobs = self._get_failed_jobs()
        if failed_jobs:
            # ids = []
            for _id,i in failed_jobs.items():
                result = self.connectionObject.Jobs.DeleteJob(_id)
                # if result == 'Success':
                #     self.takeItem(self.currentRow())
        self._update_jobs()

    def _update_current_job_info(self):
        """
        每次右击弹出菜单栏时，读取该任务的limit，并且刷新self.current_limits信息
        """
        item = self.currentItem()
        if item:
            data = item.data(5)
            job_id = data['_id']
            self.current_job_id = job_id
            ListedSlaves = self.get_ListedSlaves(job_id)
            for i in self.current_limits.keys():
                if i in ListedSlaves:
                    self.current_limits[i] = False
                else:
                    self.current_limits[i] = True


    def _set_backlist(self,new_datas):
        """
        直接设置黑名单
        """
        if not self.current_job_id:
            return
        backlist = []
        if new_datas:
            for k,v in new_datas.items():
                if not v:
                    backlist.append(k)
        result = self.connectionObject.Jobs.SetJobMachineLimitListedSlaves(self.current_job_id,backlist)
        # QMessageBox.information(None,'提示',result)
        print("黑名单:",backlist,result)


    def _select_current_index(self,item):
        data = item.data(5)
        job_id = data['_id']
        self.job_id_Signal.emit(job_id)
        print("选择的job id:",job_id)



    def filter_jobs(self,text):
        for i in range(self.count()):
            item = self.item(i)
            name = item.text()
            if text in name:
                item.setHidden(False)
            else:
                item.setHidden(True)

    def reshow_jobs(self):
        for i in range(self.count()):
            item = self.item(i)
            item.setHidden(False)

class TaskTable(QTableWidget):

    def __init__(self,task_status_map,task_status_color_map):
        super(TaskTable, self).__init__()

        self.task_status_map = task_status_map
        self.task_status_color_map = task_status_color_map

        self.setShowGrid(True)
        self.horizontalHeader().setDefaultSectionSize(100)
        self.verticalHeader().setDefaultSectionSize(50)
        self.head_list = ['id','渲染机', '状态', '帧范围', '进度']
        self.setColumnCount(len(self.head_list))

        self.setHorizontalHeaderLabels(self.head_list)
        self.setContextMenuPolicy(Qt.CustomContextMenu)



    def init_items(self):
        self.clear()
        self.setHorizontalHeaderLabels(self.head_list)
        for r in range(self.rowCount()):
            for c in range(self.columnCount()):
                item = QTableWidgetItem()
                item.setText('')
                item.setTextAlignment(Qt.AlignCenter)
                self.setItem(r, c, item)


    def set_data(self,datas):
        """
        data: {'Slave':data,'Slave':data,...} 以渲染机器名称为key的数据
        """
        self.data = datas
        if datas:
            rowcount = len(datas)
            self.setRowCount(rowcount)
            self.init_items()

            r = 0
            for k,v in self.data.items():
                color = self.task_status_color_map[v['Stat']]
                self.item(r,0).setText(str(v['TaskID']))
                self.item(r,1).setText(v['Slave'])
                self.item(r,2).setText(self.task_status_map[v['Stat']])
                self.item(r,3).setText(v['Frames'])
                self.item(r,4).setText(v['Prog'])
                for i in range(5):
                    # self.item(r,i).setBackground(QBrush(QColor(color)))
                    self.item(r,i).setTextColor(QColor(color))
                r += 1
            self.sortByColumn(0)

class RefreshMovie(QSplashScreen):
    """
    刷新过渡图
    """
    def __init__(self):
        super(RefreshMovie, self).__init__()

        self.font = QFont("", 15, 45)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowMinimizeButtonHint | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.main_layout = QVBoxLayout()
        self.gif_label = QLabel("正在载入Deadline任务数据...", self)
        self.gif_label.setFont(self.font)
        self.gif_label.setStyleSheet('color:#d6d6d6')
        self.main_layout.addWidget(self.gif_label)
        self.setLayout(self.main_layout)
        self.show()

    def mousePressEvent(self, evt):
        pass
        # 重写鼠标点击事件，阻止点击后消失
    def mouseDoubleClickEvent(self, *args, **kwargs):
        pass
        # 重写鼠标移动事件，阻止出现卡顿现象
    def enterEvent(self, *args, **kwargs):
        pass
        # 重写鼠标移动事件，阻止出现卡顿现象
    def mouseMoveEvent(self, *args, **kwargs):
        pass

class HoudiniMonitor(QWidget):
    err_Signal = Signal(int)
    def __init__(self,HOST,PORT):
        super(HoudiniMonitor, self).__init__()

        self.job_status_color_map = {0: '#727a83', 1: '#ffffff', 2: '#ff8614', 3: '#70cbff', 4: '#ef0601', 6: '#d801ef'}
        self.job_status_map = {0:'Unknown',1:'Active',2:'Suspended',3:'Completed',4:'Failed',6:'Pending'}
        self.task_status_color_map = {1: '#727a83', 2: '#f6b3f9', 3: '#ff8614', 4: '#7fe52e', 5:'#70cbff',6: '#ef0601',8:'#d801ef'}
        self.task_status_map = {1:'Unknown',2:'Queued',3:'Suspended',4:'Rendering',5:'Completed',6:'Failed',8:'Pending'}

        self.setStyleSheet("background-color:#121212;color:rgb(200,200,200)")
        

        self.username = getpass.getuser()
        # if not self.isOpen(HOST):
        #     QMessageBox.information(None,'提示','无法连接服务器')
        #     self.err_Signal.emit(1)
        #     self.close()
            
        self.connectionObject = Connect.DeadlineCon(HOST,PORT)
        self.btn_style = """QPushButton{color:black}
                                  QPushButton:hover{color:red}
                                  QPushButton{border:2px}
                                  QPushButton{background-color:#CFCFCF}
                                  QPushButton{border-radius:5px}
                                  QPushButton{padding:5px 10px}"""

        self.setWindowTitle('Houdini Monitor')
        self.resize(1380,900)
        self.main_layout = QHBoxLayout()

        self.left_layout = QVBoxLayout()
        self.filter_layout = QHBoxLayout()
        self.filter_edit = QLineEdit()
        self.filter_edit.editingFinished.connect(self._filter)
        self.filter_btn = QPushButton('查询')
        self.filter_btn.setStyleSheet(self.btn_style)
        self.filter_btn.clicked.connect(self._filter_jobs)
        self.clear_btn = QPushButton('清空')
        self.clear_btn.setStyleSheet(self.btn_style)
        self.clear_btn.clicked.connect(self._clear_filter)
        self.filter_layout.addWidget(self.filter_edit)
        self.filter_layout.addWidget(self.filter_btn)
        self.filter_layout.addWidget(self.clear_btn)
        self.job_list = JobList(self)
        self.job_list.job_id_Signal.connect(self._show_tasks)
        self.left_layout.addLayout(self.filter_layout)
        self.left_layout.addWidget(self.job_list)

        self.right_table = TaskTable(self.task_status_map,self.task_status_color_map)

        self.main_layout.addLayout(self.left_layout)
        self.main_layout.addWidget(self.right_table)
        self.setLayout(self.main_layout)
        QApplication.processEvents()

        # self.all_jobs = self.get_jobs_by_username(username='wulei')

        self.all_jobs = self.get_jobs_by_username(self.username)
        self.job_list.init_current_project_job(self.all_jobs)
        self.machines = self.get_machines()
        self.job_list.init_machine_limit(self.machines)

    def isOpen(self,ip):   #检测能否ping通机器
        """
        检测电脑是否开机
        :param ip:
        :return:
        """
        cmd = "ping {}".format(ip)
        result = os.popen(cmd)
        text = result.read()
        result.close()
        if '请求超时' in text or '无法访问目标主机' in text:#无法访问目标主机
            print('连不上')
            return False
        else:
            print('连上了')
            return True



    def get_machines(self):
        """
        获取当前所有渲染client机器名称
        """

        workers = self.connectionObject.Slaves.GetSlaveNames()
        return workers



    def get_jobs_by_username(self,username=None, ids=None):

        start = time.time()
        username = username if username else getpass.getuser()
        username = username.lower()

        if ids:
            all_active_jobs = self.connectionObject.Jobs.GetJobs(ids)
        else:
            all_active_jobs = self.connectionObject.Jobs.GetJobs()

        if not all_active_jobs:
            return []
        # print('获取到任务数量:{}'.format(len(all_active_jobs)))
        # print('all_active_jobs:',all_active_jobs)
        # print('username:',username)
        # data_list = []
        jobs = {}
        for a in all_active_jobs:
            data = {}
            Stat = a.get('Stat', 0)
            # print('Stat:',Stat,type(Stat))
            # print(data['Name'])
            if Stat in [1, 2, 3, 4, 6]:
                Props = a.get('Props', {})
                User = Props.get('User', '')
                # print('User:',User)
                ListedSlaves = Props.get('ListedSlaves', [])

                if User == username:
                    Name = Props.get('Name', '')
                    _id = a.get('_id', '')
                    data['Name'] = Name
                    data['_id'] = _id
                    data['Stat'] = Stat
                    data['ListedSlaves'] = ListedSlaves
                    data['status'] = self.job_status_map[Stat]
                    # data_list.append(data)
                    jobs[_id] = data
            # print('data:',data)
        end = time.time()

        print('执行速度:', end - start)
        return jobs



    def _filter_jobs(self):
        text = self.filter_edit.text()
        self.job_list.filter_jobs(text)


    def _clear_filter(self):
        self.filter_edit.clear()
        self.job_list.reshow_jobs()

    def _filter(self):
        if not self.filter_edit.text():
            self._clear_filter()


    def _get_tasks(self,job_id):
        """
        获取tasks
        """
        result = self.connectionObject.Tasks.GetJobTasks(job_id)
        tasks = result.get('Tasks',[])
        print('获取tasks数量：{}'.format(len(tasks)))
        task_datas = {}

        for i in tasks:
            data = {}
            data['TaskID'] = i['TaskID']
            data['Slave'] = i['Slave']
            data['Stat'] = i['Stat']
            data['Frames'] = i['Frames']
            data['Prog'] = i['Prog']
            task_datas[i['TaskID']] = data


        return task_datas

    def _show_tasks(self,job_id):
        try:
            task_datas = self._get_tasks(job_id)
            self.right_table.set_data(task_datas)
        except Exception as _show_tasks_ERR:
            print('_show_tasks_ERR：',_show_tasks_ERR)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    HOST = '192.168.1.109'
    PORT = 8081
    r_w = RefreshMovie()
    hm = HoudiniMonitor(HOST,PORT)
    # hm.err_Signal.connect(lambda : r_w.deleteLater())
    hm.show()
    r_w.finish(hm)
    r_w.deleteLater()
    sys.exit(app.exec_())